package com.neuedu.web.utils;

import org.apache.commons.beanutils.BeanUtils;

import javax.sql.DataSource;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.sql.*;

/**
 * @author 金山
 * 项目：jdbc
 * site: https://blog.fulfill.com.cn
 * 描述  数据库的工具
 * @data 2024/11/811:14
 */
public class DBUtils {
    public static final String url = "jdbc:mysql://127.0.0.1:3306/his";
    public static final String driverClass = "com.mysql.cj.jdbc.Driver";
    public static final  String username = "root";
    public static final  String password = "root";

    static{
        try {
            //1加载驱动
            Class.forName(driverClass);
        } catch (ClassNotFoundException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 工具的方法
     * @return
     */
    public static Connection getConnection() throws SQLException {


        //2建立连接

        Connection conn = DriverManager.getConnection(url, username, password);

        return conn;
    }


    /**
     * 关闭资源
     * @param conn
     * @param stmt
     * @param rs
     */

    public  static void close(Connection conn, Statement stmt, ResultSet rs){
        if(rs != null){
            try {
                rs.close();
            } catch (SQLException e) {
                throw new RuntimeException(e);
            }
        }

        if(stmt != null){
            try {
                stmt.close();
            } catch (SQLException e) {
                throw new RuntimeException(e);
            }
        }

        if(conn != null){
            try {
                conn.close();
            } catch (SQLException e) {
                throw new RuntimeException(e);
            }
        }
    }


    /**
     * 将结果集解析成一行
     * @param rs
     * @param clazz
     * @return
     * @param <T>
     * @throws NoSuchMethodException
     * @throws InvocationTargetException
     * @throws InstantiationException
     * @throws IllegalAccessException
     * @throws SQLException
     */
    public static <T>  T  analysisResultSet2PO(ResultSet rs , Class<T> clazz) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException, SQLException, NoSuchFieldException {

        if(rs ==null){
            throw new  SQLException("结果集不能为空");
        }
        if(clazz == null){
            throw new  InstantiationException("转换目标不能为空");
        }

        //使用反射机制利用class 实例化一个对那个
        Constructor<T> declaredConstructor = clazz.getDeclaredConstructor();
        T target = declaredConstructor.newInstance();

        //元数据（包含字段信息）
        ResultSetMetaData metaData = rs.getMetaData();
        int columnCount = metaData.getColumnCount();

        //通过循环将所有rs中的字段放到PO（T t）对象中 ，jdbc中的下标从1 开始
        for (int columnIndex = 1; columnIndex <= columnCount; columnIndex++) {

            //获取每个字段名
            String columnLabel = metaData.getColumnLabel(columnIndex);
            //获取字段类型  java.sql.Types
            int columnType = metaData.getColumnType(columnIndex);
            //获取rs中该字段数据
            Object columnValue = getRsColumnValue(rs, columnType,columnIndex);
            if(columnValue == null){
                continue;
            }
            //将rsColumnValue 设置到对应PO实体字段中  调用setxxxx
            setRsValue2PoColumn(rs,target,columnLabel,columnValue);
        }

        return target;
    }

    private static <T> void setRsValue2PoColumn(ResultSet rs, T target, String columnLabel, Object columnValue) throws NoSuchFieldException, IllegalAccessException, InvocationTargetException {

        //将标签名转换成 字段名，去掉下换线，除第一个单词外，其他单词首字母大写
        String fieldName = translateLabel2Field(columnLabel);

        //根据属性名查找属性 对象
        // Field declaredField = getDeclaredField(target, fieldName);

        //获取属性的java 类型，后续将 value 做转换
        // String fieldType = declaredField.getType().getName();
        // String valueType = columnValue.getClass().getName();

        // if(fieldType.equals(valueType)){
        //     //如果类型一直  如java中  java.lang.Integer    Object columnValue 也是 java.lang.Integer 可以直接复制
        //     declaredField.set(target,columnValue);
        // }else{
        //     //如果声明的类型不一致 则需要 转换一下，如 值的类型 是 java.lang.Long  而   java中属性是
        //     switch ()
        // }

        //暂时不处理了，使用BeanUtils 工具进行设置属性
        // System.out.println("fieldName = " + fieldName);
        BeanUtils.setProperty(target,fieldName,columnValue);

    }


    /**
     * //如果列名中包含下划线  则去掉下划线，后面字母变成大写 userid->userid   user_name-> userName  hello_world_haha -> helloWorldHaha
     * @param columnLabel
     * @return
     */
    public static String translateLabel2Field(String columnLabel){
        //初始化一个准备放置 属性名的容器
        StringBuffer fieldName = new StringBuffer(columnLabel.length());

        if(columnLabel.contains("_")){
            String[] words = columnLabel.split("_");
            fieldName.append(words[0]); // 第一个单词原样放置
            for (int i = 1; i < words.length; i++) {
                String firstLetter = words[i].substring(0,1).toLowerCase();
                String otherLesster = words[i].substring(1);
                fieldName.append(firstLetter+otherLesster);
            }
        }else{
            fieldName.append(columnLabel);
        }

        return fieldName.toString();
    }

    /**
     * 利用反射机制  获取 列名对应的字段
     * @param target
     * @param fieldName
     * @return
     * @param <T>
     */
    public static <T> Field  getDeclaredField(T target,String fieldName) throws NoSuchFieldException {


        //得到对象的 字节码class对象
        Class<T> clazz = (Class<T>) target.getClass();

        //获取所有该该类的所有方法，遍历查找，比较笨 弃用
        // Field[] declaredFields = clazz.getDeclaredFields();
        // for (Field declaredField : declaredFields) {
        //     if(declaredField.getName().equalsIgnoreCase(columnLabel)){
        //         return declaredField;
        //     }
        // }
        Field declaredField = clazz.getDeclaredField(fieldName);


        return declaredField;
    }

    /**
     * 获取对应字段的值
     * @param rs
     * @param columnIndex
     * @return
     */
    public static Object getRsColumnValue(ResultSet rs,int columnType, int columnIndex ) throws SQLException {

        Object value = null;
        // System.out.printf("columnLabel:%s\tcolumnType:%d\r\n",columnLabel,columnType);
        switch (columnType){
            case Types.BIGINT:
                value = rs.getLong(columnIndex);
                break;
            case Types.CHAR:
            case Types.VARCHAR:
                value = rs.getString(columnIndex);
                break;
            case Types.INTEGER:
                value = rs.getInt(columnIndex);
                break;
            case Types.TIMESTAMP:
                value = rs.getTimestamp(columnIndex);
            break;
            case Types.DATE:
                value = rs.getDate(columnIndex);
                break;
            case Types.DECIMAL:
            case Types.DOUBLE:
                value = rs.getDouble(columnIndex);
                break;
            default:
                value = rs.getObject(columnIndex);
                break;
        }

        return  value;


    }








    /**
     * 绑定变量
     * @param stmt
     * @param params
     */
    public static void  bingStmtVar(PreparedStatement stmt  , Object... params){
        if(params != null && params.length >0){
            int index = 0;
            for (Object param : params) {
                try {
                    stmt.setObject(++index,param);
                } catch (SQLException e) {
                    throw new RuntimeException(e);
                }
            }
        }
    }


}





